package com.fengwk.support.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fengwk.support.exception.BaseException;

/**
 * 工具类:文件下载
 */
public class DownloadUtils {

	private DownloadUtils() {
		throw new BaseException("can not instance " + getClass());
	}

	/**
	 * 下载
	 * 
	 * @param response
	 * @param fileName 文件名称
	 * @param bytes 文件的二进制byte[]
	 */
	public static void download(HttpServletResponse response, String fileName, byte[] bytes) {
		response.setHeader("Content-Length", bytes.length + "");
		InputStream is = new ByteArrayInputStream(bytes);
		download(response, fileName, is);
	}

	/**
	 * 下载工具方法
	 * 
	 * @param response
	 * @param fileName 文件名称
	 * @param filePath 文件路径
	 */
	public static void downLoad(HttpServletResponse response, String fileName, String filePath) {
		File file = new File(filePath);
		download(response, fileName, file);
	}

	/**
	 * 下载工具方法
	 * 
	 * @param response
	 * @param fileName 文件名称
	 * @param file 输出的文件
	 */
	public static void download(HttpServletResponse response, String fileName, File file) {
		if (fileName == null) {
			fileName = file.getName();
		}
		try {
			// 设置文件大小
			response.setHeader("Content-Length", file.length() + "");
			// 读取文件至流中
			InputStream fileInputStream = new FileInputStream(file);
			// 文件下载
			download(response, fileName, fileInputStream);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 下载工具方法,支持断点下载,推荐使用
	 * 
	 * @param request
	 * @param response
	 * @param fileName
	 * @param file
	 */
	public static void download(HttpServletRequest request, HttpServletResponse response, String fileName, File file) {
		if (file == null) {
			return;
		}
		if (fileName == null) {
			fileName = file.getName();
		}
		FileInputStream is = null;
		try {
			is = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		download(request, response, fileName, file.length(), is);
	}

	/**
	 * 下载工具方法,支持断点下载,推荐使用
	 * 
	 * @param request
	 * @param response
	 * @param fileName
	 * @param fileLength 需要传入该参数
	 * @param is
	 */
	public static void download(HttpServletRequest request, HttpServletResponse response, String fileName, Long fileLength, InputStream is) {
		if (is == null) {
			return;
		}
		OutputStream os = null;
		try {
			long fileSize = fileLength;
			response.setHeader("Accept-Ranges", "bytes");
			String range = request.getHeader("Range");
			int status = HttpServletResponse.SC_OK; // 返回的状态码，默认200，首次下载
			// 如果range下载区域为空，则首次下载
			if (range == null) {
				range = "bytes=0-";
			} else {
				// 通过下载区域下载，使用206状态码支持断点续传
				status = HttpServletResponse.SC_PARTIAL_CONTENT;
			}
			long start = 0, end = 0;
			if (null != range && range.startsWith("bytes=")) {
				String[] values = range.split("=")[1].split("-");
				start = Integer.parseInt(values[0]);
				// 如果服务器端没有设置end结尾，默认取下载全部
				if (values.length == 1) {
					end = fileSize;
				} else {
					end = Integer.parseInt(values[1]);
				}
			}
			// 此次数据响应大小
			long responseSize = 0;
			if (end != 0 && end > start) {
				responseSize = end - start + 1;
				// 返回当前连接下载的数据大小,也就是此次数据传输大小
				response.addHeader("Content-Length", "" + (responseSize));
			} else {
				responseSize = Integer.MAX_VALUE;
			}
			byte[] buffer = new byte[1024];
			// 设置响应状态码
			response.setStatus(status);
			if (status == HttpServletResponse.SC_PARTIAL_CONTENT) {
				// 设置断点续传的Content-Range传输字节和总字节
				response.addHeader("Content-Range", "bytes " + start + "-" + (fileSize - 1) + "/" + fileSize);
			}
			// 设置响应客户端内容类型
			setResponseHead(response, fileName);
			// 当前需要下载文件的大小
			int needSize = (int) responseSize;
			if (start != 0) {
				// 跳已经传输过的字节
				is.skip(start);
			}
			os = response.getOutputStream();
			while (needSize > 0) {
				int len = is.read(buffer);
				if (needSize < buffer.length) {
					os.write(buffer, 0, needSize);
				} else {
					os.write(buffer, 0, len);
					// 如果读取文件大小小于缓冲字节大小，表示已写入完，直接跳出
					if (len < buffer.length) {
						break;
					}
				}
				// 不断更新当前可下载文件大小
				needSize -= buffer.length;
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			IoUtils.close(is);
		}
	}
	
	/**
	 * 下载工具方法,展示文件大小(需先读取文件大小,效率较低,不推荐使用)
	 * 
	 * @param response
	 * @param fileName 文件名称
	 * @param fileInputStream 输出文件的流
	 */
	@Deprecated
	public static void downloadWithFileSize(HttpServletResponse response, String fileName, InputStream is) {
		// 转为数组
		byte[] bytes = IoUtils.toBytes(is);
		// 关闭读入流
		IoUtils.close(is);
		response.setHeader("Content-Length", bytes.length + "");
		// 下载
		download(response, fileName, IoUtils.toInputStream(bytes));
	}

	/**
	 * 下载工具方法
	 * 
	 * @param response
	 * @param fileName 文件名称
	 * @param fileInputStream  输出文件的流
	 */
	public static void download(HttpServletResponse response, String fileName, InputStream is) {
		// 设置响应头
		setResponseHead(response, fileName);
		// 写出文件
		outResponse(response, is);
	}

	/**
	 * 输出文件
	 * 
	 * @param response
	 * @param is
	 */
	private static void outResponse(HttpServletResponse response, InputStream is) {
		OutputStream out = null;
		try {
			out = response.getOutputStream();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (out != null) {
			// 缓存1024
			byte[] buff = new byte[1024];
			int len = 0;
			// 输出文件
			try {
				while ((len = is.read(buff)) != -1) {
					out.write(buff, 0, len);
				}
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				// 关闭输入流
				IoUtils.close(is);
			}
		}
	}

	/**
	 * 设置下载文件的响应头
	 * 
	 * @param response
	 * @param fileName
	 */
	private static void setResponseHead(HttpServletResponse response, String fileName) {
		response.setCharacterEncoding("utf-8");
		response.setContentType("application/octet-stream;charset=utf-8");
		try {
			response.setHeader("Content-Disposition",
					"attachment;filename=\"" + URLEncoder.encode(fileName, "utf-8") + "\"");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

}
